﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using WebService.VO;

namespace WebService.DAO
{
    public class GDirections
    {
        public static RouteVO parse(string startpoint, string endpoint)
        {
            string jsonURL = "http://maps.google.com/maps/api/directions/json?";
            StringBuilder url = new StringBuilder(jsonURL);
            url.Append("origin=");
            url.Append(startpoint);
            url.Append("&destination=");
            url.Append(endpoint);
            url.Append("&sensor=true&mode=driving");
            url.Append("&language=vi");
            int distance = 0;
            // turn the stream into a string
            WebClient c = new WebClient();
            c.Encoding = Encoding.UTF8;
            var data = c.DownloadString(url.ToString());
            Console.WriteLine(url);
            //Create an empty route
            RouteVO route = new RouteVO();
            try
            {
                //Tranform the string into a json object
                JObject json = JObject.Parse(data);
                //Get the status.
                String status = json["status"].ToString();
                Console.WriteLine("Status: " + status);
                if (status.Equals("ZERO_RESULTS") || status.Equals("NOT_FOUND"))
                {
                    return null;
                }
                //Get the routes Array.
                JArray arrayRoute = JArray.FromObject(json["routes"]);
                //Get the route object.
                JObject jsonRoute = (JObject)arrayRoute[0];
                // Take all legs from the route
                JArray legs = JArray.FromObject(jsonRoute["legs"]);
                // Grab first leg
                JObject leg = (JObject)legs[0];
                //Get the steps for this leg
                JArray steps = JArray.FromObject(leg["steps"]);
                //Number of steps for use in for loop
                int numSteps = steps.Count;
                //Set the name of this route using the start & end addresses
                route.Start_address = leg["start_address"].ToString();
                Console.WriteLine("Start_address: " + route.Start_address);
                route.Dest_address = leg["end_address"].ToString();
                Console.WriteLine("End_address: " + route.Dest_address);
                //Get google's copyright notice (tos requirement)
                route.Copyright = jsonRoute["copyrights"].ToString();
                Console.WriteLine("Copyrights: " + route.Copyright);
                //Get the total length of the route.
                route.Length = Convert.ToInt32(JObject.FromObject(leg["distance"])["value"].ToString());
                Console.WriteLine("Length: " + route.Length);
                //Get the total distance of the route by value.
                route.Distance_value = Convert.ToInt32(JObject.FromObject(leg["distance"])["value"].ToString());
                Console.WriteLine("Distance_value: " + route.Distance_value);
                //Get the total distance of the route by text.
                route.Distance_text = JObject.FromObject(leg["distance"])["text"].ToString();
                Console.WriteLine("Distance_text: " + route.Distance_text);
                //Get the total durtation of the route by value.
                route.Duration_value = route.Distance_value = Convert.ToInt32(JObject.FromObject(leg["duration"])["value"].ToString());
                Console.WriteLine("Duration_value: " + route.Duration_value);
                //Get the total duration of the route by text.
                route.Duration_text = JObject.FromObject(leg["duration"])["text"].ToString();
                Console.WriteLine("Duration_text: " + route.Duration_text);
                //Get any warnings provided (tos requirement)
                //if (!jsonRoute.getJSONArray("warnings").isNull(0)) {
                //        route.setWarning(jsonRoute.getJSONArray("warnings").getString(0));
                //}
                /* Loop through the steps, creating a segment for each one and
                 * decoding any polylines found as we go to add to the route object's
                 * map array. Using an explicit for loop because it is faster!
                 */
                //System.out.println("Google JSON Parser - " + numSteps);
                List<SegmentVO> segments = new List<SegmentVO>();
                for (int i = 0; i < numSteps; i++)
                {
                    //Create an empty segment
                    SegmentVO segment = new SegmentVO();
                    //Get the individual step
                    JObject step = (JObject)steps[i];
                    Console.WriteLine("Step: " + i);
                    //Get the start position for this step and set it on the segment
                    JObject start = JObject.FromObject(step["start_location"]);
                    LatLng position = new LatLng(Convert.ToDouble(start["lat"].ToString()),
                            Convert.ToDouble(start["lng"].ToString()));
                    segment.Start = position;
                    Console.WriteLine("Start: " + segment.Start.Latitude + "," + segment.Start.Longitude);
                    //Set the length of this segment in metres
                    int length = Convert.ToInt32(JObject.FromObject(step["distance"])["value"].ToString());
                    distance += length;
                    segment.Length = length;
                    Console.WriteLine("Segment.Length: " + segment.Length);
                    segment.Distance = distance / 1000;
                    Console.WriteLine("Segment.Distance: " + segment.Distance);
                    //Strip html from google directions and set as turn instruction
                    segment.Instruction = step["html_instructions"].ToString();
                    Console.WriteLine("Segment.Instroductions: " + segment.Instruction);
                    //Retrieve & decode this segment's polyline and add it to the route.
                    JObject polyline = JObject.FromObject(step["polyline"]);
                    route.Points.AddRange(decodePolyLine(polyline["points"].ToString()));
                    Console.WriteLine("Polyline: " + polyline["points"].ToString());
                    segments.Add(segment);
                }
                //Push a copy of the segment to the route
                route.Segments = segments;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message, "Google JSON Parser");
            }
            return route;
        }
        /**
     * Decode a polyline string into a list of GeoPoints.
     * @param poly polyline encoded string to decode.
     * @return the list of GeoPoints represented by this polystring.
     */

        private static List<LatLng> decodePolyLine(string poly) 
        {
    	
            int len = poly.Length;
            int index = 0;
            List<LatLng> decoded = new List<LatLng>();
            double lat = 0;
            double lng = 0;

            while (index < len) {
            int b;
            int shift = 0;
            int result = 0;
            do {
                    b = poly[index++] - 63;
                    result |= (b & 0x1f) << shift;
                    shift += 5;
            } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;

            shift = 0;
            result = 0;
            do {
                    b = poly[index++] - 63;
                    result |= (b & 0x1f) << shift;
                    shift += 5;
            } while (b >= 0x20);
                    int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
                    lng += dlng;

            decoded.Add(new LatLng(
                        (lat*1E-5),  (lng* 1E-5)));
            }

            return decoded;
        }
    }
}